/*
   Demo Name:  Direct Input Demo
	  Author:  Allen Sherrod
	 Chapter:  Ch 10
*/


#include"CDirectInput.h"


CDirectInputSystem* gThis = NULL;


BOOL gJSEnumDeviceCallBack(const DIDEVICEINSTANCE* inst, void* pData)
{
	return gThis->EnumDeviceCallBack(inst, pData);
}


CDirectInputSystem::CDirectInputSystem()
{
	// Initialize objects...
	m_Keyboard = NULL;
	m_Mouse = NULL;
	m_GameControl = NULL;
	m_controllerFound = false;

	memset(m_name, 0, sizeof(char) * 256);
	memset(&m_Mouse_State, 0, sizeof(m_Mouse_State));
	memset(&m_oldMouse_State, 0, sizeof(m_oldMouse_State));
	memset(m_Keys, 0, sizeof(m_Keys));
	memset(&m_oldKeys, 0, sizeof(m_oldKeys));
	memset(&m_GC_State, 0, sizeof(m_GC_State));
	memset(&m_oldGC_State, 0, sizeof(m_oldGC_State));

	m_numButtons = 0;
	m_hwnd = 0;
	m_xMPos = 0;
	m_yMPos = 0;
	m_zMPos = 0;
	m_xGCPos = 0;
	m_yGCPos = 0;
	m_xGCPos2 = 0;
	m_yGCPos2 = 0;
}


CDirectInputSystem::~CDirectInputSystem()
{
	// Shut everything down.
	Shutdown();
}


bool CDirectInputSystem::Initialize(HWND hwnd, HINSTANCE hInstance, bool mouseExclusive)
{
	// Save copies.
	m_hwnd = hwnd;
	gThis = this;

	// Create input system.
	if (FAILED(DirectInput8Create(hInstance, DIRECTINPUT_VERSION, IID_IDirectInput8,
		(void**)&m_InputSystem, NULL))) return false;

	// Initialize the keyboard.
	if (FAILED(m_InputSystem->CreateDevice(GUID_SysKeyboard, &m_Keyboard, NULL)))
		return false;

	if (FAILED(m_Keyboard->SetDataFormat(&c_dfDIKeyboard))) return false;
	if (FAILED(m_Keyboard->SetCooperativeLevel(m_hwnd, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE)))
		return false;
	if (FAILED(m_Keyboard->Acquire())) return false;

	// Clear keys will clear out the array of keys we have.
	memset(m_Keys, 0, sizeof(m_Keys));


	// Initialize the Mouse.
	DWORD flags;

	if (FAILED(m_InputSystem->CreateDevice(GUID_SysMouse, &m_Mouse, NULL))) return false;
	if (FAILED(m_Mouse->SetDataFormat(&c_dfDIMouse))) return false;

	if (mouseExclusive) flags = DISCL_FOREGROUND | DISCL_EXCLUSIVE | DISCL_NOWINKEY;
	else flags = DISCL_FOREGROUND | DISCL_NONEXCLUSIVE;

	if (FAILED(m_Mouse->SetCooperativeLevel(m_hwnd, flags))) return false;
	if (FAILED(m_Mouse->Acquire())) return false;


	// Initialize the game controller.
	DIPROPRANGE range;
	DIDEVCAPS caps;

	m_InputSystem->EnumDevices(DI8DEVCLASS_GAMECTRL, (LPDIENUMDEVICESCALLBACK)gJSEnumDeviceCallBack,
		NULL, DIEDFL_ATTACHEDONLY);

	if (!m_controllerFound) return false;

	range.diph.dwSize = sizeof(DIPROPRANGE);
	range.diph.dwHeaderSize = sizeof(DIPROPHEADER);
	range.diph.dwHow = DIPH_BYOFFSET;
	range.lMin = -1000;
	range.lMax = 1000;
	range.diph.dwObj = DIJOFS_X;
	m_GameControl->SetProperty(DIPROP_RANGE, &range.diph);
	range.diph.dwObj = DIJOFS_Y;
	m_GameControl->SetProperty(DIPROP_RANGE, &range.diph);

	if (SUCCEEDED(m_GameControl->GetCapabilities(&caps))) m_numButtons = caps.dwButtons;
	else m_numButtons = 4;

	return true;
}


BOOL CDirectInputSystem::EnumDeviceCallBack(const DIDEVICEINSTANCE* inst, void* pData)
{
	// Set to the first device found.
	if (SUCCEEDED(m_InputSystem->CreateDevice(inst->guidInstance, &m_GameControl, NULL)))
	{
		if (SUCCEEDED(m_GameControl->SetDataFormat(&c_dfDIJoystick2)))
			if (SUCCEEDED(m_GameControl->SetCooperativeLevel(m_hwnd, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE)))
				if (SUCCEEDED(m_GameControl->Acquire()))
				{
					strcpy(m_name, (char*)inst->tszProductName);
					m_controllerFound = true;
					return DIENUM_STOP;
				}
	}

	// Return continue to try to init other connected devices.
	return DIENUM_CONTINUE;
}


bool CDirectInputSystem::UpdateDevices()
{
	// Get the device state.
	if (m_Mouse)
	{
		// Save old state for input comparing.
		memcpy(&m_oldMouse_State, &m_Mouse_State, sizeof(m_Mouse_State));

		// If error getting device state, re-aquire.
		if (FAILED(m_Mouse->GetDeviceState(sizeof(DIMOUSESTATE), &m_Mouse_State)))
		{
			if (FAILED(m_Mouse->Acquire())) return false;
			if (FAILED(m_Mouse->GetDeviceState(sizeof(DIMOUSESTATE), &m_Mouse_State)))
				return false;
		}

		m_xMPos += m_Mouse_State.lX;
		m_yMPos += m_Mouse_State.lY;
		m_zMPos = m_Mouse_State.lZ;
	}

	if (m_Keyboard)
	{
		// Save old state for input comparing.
		memcpy(m_oldKeys, m_Keys, sizeof(m_Keys));

		// If error getting device state, re-aquire.
		if (FAILED(m_Keyboard->GetDeviceState(sizeof(m_Keys), (LPVOID)m_Keys)))
		{
			if (FAILED(m_Keyboard->Acquire())) return false;
			if (FAILED(m_Keyboard->GetDeviceState(sizeof(m_Keys), (LPVOID)m_Keys)))
				return false;
		}
	}

	if (m_GameControl)
	{
		m_GameControl->Poll();

		// Save old state for input comparing.
		memcpy(&m_oldGC_State, &m_GC_State, sizeof(m_GC_State));

		// If error getting device state, re-aquire.
		if (FAILED(m_GameControl->GetDeviceState(sizeof(DIJOYSTATE2), &m_GC_State)))
		{
			if (FAILED(m_GameControl->Acquire())) return false;
			if (FAILED(m_GameControl->GetDeviceState(sizeof(DIJOYSTATE2), &m_GC_State)))
				return false;
		}

		m_xGCPos = m_GC_State.lX;
		m_yGCPos = m_GC_State.lY;

		m_xGCPos2 = m_GC_State.lZ;
		m_yGCPos2 = m_GC_State.lRz;
	}

	return true;
}


int CDirectInputSystem::KeyUp(unsigned int key)
{
	// If the key in the variable is not pressed then return false.
	return (!(m_Keys[key] & 0x80) && m_Keys[key] != m_oldKeys[key]);
}


int CDirectInputSystem::KeyDown(unsigned int key)
{
	// If the key in the variable is not pressed then return false.
	return m_Keys[key] & 0x80;
}


int CDirectInputSystem::MouseButtonUp(unsigned int button)
{
	// If the button is not clicked we return false.
	return (!(m_Mouse_State.rgbButtons[button] & 0x80) &&
		m_Mouse_State.rgbButtons[button] != m_oldMouse_State.rgbButtons[button]);
}


int CDirectInputSystem::MouseButtonDown(unsigned int button)
{
	// If the button is clicked we return true.
	return m_Mouse_State.rgbButtons[button] & 0x80;
}


int CDirectInputSystem::ControllerButtonUp(unsigned int button)
{
	if (button < 0 || button >= m_numButtons) return 0;
	return (!(m_GC_State.rgbButtons[button] & 0x80) &&
		m_GC_State.rgbButtons[button] != m_oldGC_State.rgbButtons[button]);
}


int CDirectInputSystem::ControllerButtonDown(unsigned int button)
{
	if (button < 0 || button >= m_numButtons) return 0;
	return m_GC_State.rgbButtons[button] & 0x80;
}


POINT CDirectInputSystem::GetMousePos()
{
	POINT pos;

	pos.x = m_xMPos;
	pos.y = m_yMPos;
	return pos;
}


long CDirectInputSystem::GetMouseWheelPos()
{
	return m_zMPos;
}


POINT CDirectInputSystem::GetLeftStickPos()
{
	POINT pos;

	pos.x = m_xGCPos;
	pos.y = m_yGCPos;
	return pos;
}


POINT CDirectInputSystem::GetRightStickPos()
{
	POINT pos;

	pos.x = m_xGCPos2;
	pos.y = m_yGCPos2;
	return pos;
}


void CDirectInputSystem::Shutdown()
{
	// Delete each object...
	if (m_Keyboard)
	{
		m_Keyboard->Unacquire();
		m_Keyboard->Release();
		m_Keyboard = NULL;
	}

	if (m_Mouse)
	{
		m_Mouse->Unacquire();
		m_Mouse->Release();
		m_Mouse = NULL;
	}

	if (m_GameControl)
	{
		m_GameControl->Unacquire();
		m_GameControl->Release();
		m_GameControl = NULL;
	}

	if (m_InputSystem)
	{
		m_InputSystem->Release();
		m_InputSystem = NULL;
	}
}